/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package intopo.evogame.partnerselection;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;

/**
 * Contains fields and methods to store the data generated by a simulation run.  The data is written in binary files.  The data has been normalised as presented in the following figure:
 *
 * <img src="database.png">
 * 
 * @author <a href="mailto:mariano.pedro@gmail.com">Pedro Mariano</a>
 *
 * @version 1.0 __DATE__
 */
public class Database
{
	/**
	 * File where chromosome information is written.
	 * <p> The record format is:
	 * <pre>
	 * | byte 1 | byte 2 | byte 3 | byte 4 | byte 5 | byte 6 | byte 7 | byte 8 |
	 * |     chromosome identification     |  pool  |     utility threshold
	 * |            PRIMARY KEY            |  size  |
	 *
	 * | byte 9 | byte 10| byte 11| byte 12| byte 13| byte 14| byte 15| byte 16|
	 *          |               delta               |bits per|    strategy     ...
	 *          |                                   |probabil|
	 * </pre>
	 */
	public DataOutputStream chromosome;
	/**
	 * File where player information is written.
	 * <p> The record format is:
	 * <pre>
	 * | byte 1 | byte 2 | byte 3 | byte 4 | byte 5 | byte 6 | byte 7 | byte 8 |
	 * |       player identification       |reference chromosome identification|
	 * |            PRIMARY KEY            |            FOREIGN KEY            |
	 *
	 * | byte 9 | byte 10| byte 11| byte 12| byte 13| byte 14| byte 15| byte 16|
	 * |   reference node identification   |     reference parent player       |
	 * |            FOREIGN KEY            |            FOREIGN KEY            |
	 *
	 * | byte 17| byte 18| byte 19| byte 20| byte 21| byte 22| byte 23| byte 24|
	 * |             born time             |             death time            |
	 * |                                   |                                   |
	 * </pre>
	 */
	public DataOutputStream player;
	/**
	 * File where player death is written.
	 * <p> The record format is:
	 * <pre>
	 * | byte 1 | byte 2 | byte 3 | byte 4 | byte 5 | byte 6 | byte 7 | byte 8 |
	 * |       player identification       |             death time            |
	 * |            PRIMARY KEY            |                                   |
	 * </pre>
	 */
	public DataOutputStream playerDeath;
	/**
	 * File where player node is written.
	 * <p> The record format is:
	 * <pre>
	 * | byte 1 | byte 2 | byte 3 | byte 4 | byte 5 | byte 6 | byte 7 | byte 8 |
	 * |       player identification       |   reference node identification   |
	 * |            PRIMARY KEY            |          FOREIGN KEY              |
	 * </pre>
	 */
	public DataOutputStream playerNode;
	/**
	 * File where player's phenotype information is written.
	 * <p> The record format is:
	 * <pre>
	 * | byte 1 | byte 2 | byte 3 | byte 4 | byte 5 | byte 6 | byte 7 | byte 8 |
	 * |  reference player identification  |                time               |
	 * |     PRIMARY KEY   FOREIGN KEY     |            PRIMARY KEY            |
	 *
	 * | byte 9 | byte 10| byte 11| byte 12|
	 * |              energy               |
	 * |                                   |
	 * </pre>
	 */
	public DataOutputStream playerPhenotype;
	/**
	 * File where node information is written.
	 * <p> The record format is:
	 * <pre>
	 * | byte 1 | byte 2 | byte 3 | byte 4 | byte 5 | byte 6 | byte 7 | byte 8 |
	 * |        node identification        |         carrying capacity         |
	 * |            PRIMARY KEY            |                                   |
	 * </pre>
	 */
	public DataOutputStream node;
	/**
	 * File where information about who selected who is written.
	 * <p> The record format is:
	 * <pre>
	 * | byte 1 | byte 2 | byte 3 | byte 4 | byte 5 | byte 6 | byte 7 | byte 8 |
	 * |                time               |   reference selecting player id   |
	 * |            PRIMARY KEY            |     PRIMARY KEY   FOREIGN KEY     |
	 *
	 * | byte 9 | byte 10| byte 11| byte 12|
	 * |    reference selected player id   |
	 * |     PRIMARY KEY   FOREIGN KEY     |
	 * </pre>
	 */
	public DataOutputStream gamesSelected;
	/**
	 * File where information about number of times two players played a game in a simulation round.
	 * <p> The record format is:
	 * <pre>
	 * | byte 1 | byte 2 | byte 3 | byte 4 | byte 5 | byte 6 | byte 7 | byte 8 |
	 * |                time               |     reference first player id     |
	 * |            PRIMARY KEY            |     PRIMARY KEY   FOREIGN KEY     |
	 *
	 * | byte 9 | byte 10| byte 11| byte 12| byte 13| byte 14| byte 15| byte 16|
	 * |     reference second player id    |               count               |
	 * |     PRIMARY KEY   FOREIGN KEY     |                                   |
	 * </pre>
	 */
	public DataOutputStream gamesPlayed;
	public DataOutputStream probabilityVectorCombination;
	public DataOutputStream probabilityVectorCombinationPartner;

	transient private DataOutputStream[] streams;
	/**
	 * This class follows the singleton paradigm.
	 */
	private Database ()
	{
	}
	static public Database singleton = new Database ();
	/**
	 * Size of the buffer associated with each table of the database.
	 */
	static final private int BUFFER_SIZE = 4096;
	/**
	 * Opens the tables of the database for writing.
	 * @throws java.io.FileNotFoundException
	 */
	public void open (boolean openProbabilityVectorTablesFlag)
		throws FileNotFoundException
	{
		if (openProbabilityVectorTablesFlag) {
			this.streams = new DataOutputStream [10];
		}
		else {
			this.streams = new DataOutputStream [8];
		}

		this.streams [0] = this.chromosome      = new DataOutputStream (new BufferedOutputStream (new FileOutputStream ("chromosome.tab"), Database.BUFFER_SIZE));
		this.streams [1] = this.gamesPlayed     = new DataOutputStream (new BufferedOutputStream (new FileOutputStream ("games-played.tab"), Database.BUFFER_SIZE));
		this.streams [2] = this.gamesSelected   = new DataOutputStream (new BufferedOutputStream (new FileOutputStream ("games-selected.tab"), Database.BUFFER_SIZE));
		this.streams [3] = this.node            = new DataOutputStream (new BufferedOutputStream (new FileOutputStream ("node.tab"), Database.BUFFER_SIZE));
		this.streams [4] = this.player          = new DataOutputStream (new BufferedOutputStream (new FileOutputStream ("player.tab"), Database.BUFFER_SIZE));
		this.streams [5] = this.playerDeath     = new DataOutputStream (new BufferedOutputStream (new FileOutputStream ("player-death.tab"), Database.BUFFER_SIZE));
		this.streams [6] = this.playerNode      = new DataOutputStream (new BufferedOutputStream (new FileOutputStream ("player-node.tab"), Database.BUFFER_SIZE));
		this.streams [7] = this.playerPhenotype = new DataOutputStream (new BufferedOutputStream (new FileOutputStream ("player-phenotype.tab"), Database.BUFFER_SIZE));
		if (openProbabilityVectorTablesFlag) {
			this.streams [8] = this.probabilityVectorCombination = new DataOutputStream (new BufferedOutputStream (new FileOutputStream ("probability-vector-combination.tab"), Database.BUFFER_SIZE));
			this.streams [9] = this.probabilityVectorCombinationPartner = new DataOutputStream (new BufferedOutputStream (new FileOutputStream ("probability-vector-combination-partner.tab"), Database.BUFFER_SIZE));
		}
 	}
	/**
	 * Closes the streams where the data was written.
	 *
	 * <p> After the simulation has finished, we complete the data in table <i>Player</i>.
	 * @throws java.io.IOException
	 */
	void close ()
		throws IOException
	{
		for (DataOutputStream dos : this.streams) {
			dos.flush ();
			dos.close ();
		}
		RandomAccessFile ras = new RandomAccessFile ("player.tab", "rw");
		DataInputStream dis;
		int playerID;
		//
		dis = new DataInputStream (new BufferedInputStream (new FileInputStream ("player-death.tab")));
		while (true) {
			try {
				playerID = dis.readInt ();
				int playerDeathTime = dis.readInt ();
				ras.seek (playerID * 24 + 20);
				ras.writeInt (playerDeathTime);
			}
			catch (EOFException e) {
				break;
			}
		}
		dis.close ();
		//
		dis = new DataInputStream (new BufferedInputStream (new FileInputStream ("player-node.tab")));
		while (true) {
			try {
				playerID = dis.readInt ();
				int playerNode = dis.readInt ();
				ras.seek (playerID * 24 + 8);
				ras.writeInt (playerNode);
			}
			catch (EOFException e) {
				break;
			}
		}
		dis.close ();
		ras.close ();
	}
}

// Local Variables:
// mode: java
// mode: flyspell-prog
// ispell-local-dictionary: "british"
// End:
